Skip to content

feat: Make MCP server actions more robust#820

Merged
kingston merged 4 commits intomainfrom
kingston/eng-1096-mcp-server-improvements-and-fixes
Mar 13, 2026
Merged

feat: Make MCP server actions more robust#820
kingston merged 4 commits intomainfrom
kingston/eng-1096-mcp-server-improvements-and-fixes

Conversation

@kingston
Copy link
Collaborator

@kingston kingston commented Mar 13, 2026

MCP server improvements: apply fixRefDeletions and applyDefinitionFixes when staging changes, add entity search action, expose auto-fix suggestions with apply-fix action, add plugin management actions (list, configure, disable), blacklist plugin entity type from generic entity operations

Summary by CodeRabbit

  • New Features

    • Entity search (query + optional type filter)
    • Plugin management: list, configure, disable
    • Apply auto-fix suggestions to draft definitions
  • Bug Fixes

    • Improved entity path resolution for discriminated-union-array and nested fields
  • Changes

    • Plugins excluded from generic entity operations; draft validation now surfaces fix suggestions and warnings and supports apply-fix flow
  • Tests

    • Extensive new unit and integration tests covering actions, lifecycle, and validation utilities

@changeset-bot
Copy link

changeset-bot bot commented Mar 13, 2026

🦋 Changeset detected

Latest commit: 2c0cd6c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@baseplate-dev/project-builder-server Patch
@baseplate-dev/project-builder-lib Patch
@baseplate-dev/create-project Patch
@baseplate-dev/project-builder-cli Patch
@baseplate-dev/project-builder-common Patch
@baseplate-dev/project-builder-dev Patch
@baseplate-dev/project-builder-web Patch
@baseplate-dev/plugin-auth Patch
@baseplate-dev/plugin-email Patch
@baseplate-dev/plugin-queue Patch
@baseplate-dev/plugin-rate-limit Patch
@baseplate-dev/plugin-storage Patch
@baseplate-dev/project-builder-test Patch
@baseplate-dev/code-morph Patch
@baseplate-dev/core-generators Patch
@baseplate-dev/fastify-generators Patch
@baseplate-dev/react-generators Patch
@baseplate-dev/sync Patch
@baseplate-dev/tools Patch
@baseplate-dev/ui-components Patch
@baseplate-dev/utils Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds auto-fix support and fix application for draft project definitions, plugin management and entity search actions, refactors draft validation/save pipeline to produce fix metadata and warnings, extends schema path model to include array/record path elements, and updates tests and action registry accordingly.

Changes

Cohort / File(s) Summary
Schema Path Model
packages/project-builder-lib/src/parser/walk-schema-structure.ts, packages/project-builder-lib/src/parser/walk-schema-structure.unit.test.ts
Introduce { type: 'array' } and { type: 'record' } SchemaPathElement variants; arrays and records now emit path elements during traversal; tests updated.
Entity Navigation & Type Mapping
packages/project-builder-lib/src/tools/entity-service/entity-navigation.ts, packages/project-builder-lib/src/tools/entity-service/entity-type-map.ts
Navigation now errors on non-deterministic array/record path elements; child relativePath computation slices one extra element to account for discriminated-union/array descriptors.
Entity ID Tests
packages/project-builder-lib/src/tools/assign-entity-ids.unit.test.ts
New unit tests validating generation/preservation of entity and nested-field IDs across scenarios.
Draft Validation & Auto-Fix
packages/project-builder-server/src/actions/definition/validate-draft.ts, packages/project-builder-server/src/actions/definition/validate-draft.unit.test.ts
Add fix-and-validate workflow: fixAndValidateDraftDefinition, validateAndSaveDraft, generateFixId, mapIssueToOutput, fix metadata on issues (fixLabel, fixId), and CLI output helper; tests for deterministic fix IDs and mapping.
Apply Fix Action
packages/project-builder-server/src/actions/definition/apply-fix.action.ts, packages/project-builder-server/src/actions/definition/draft-lifecycle.int.test.ts
New apply-fix service action that locates a fix by fixId, applies it immutably to the draft, serializes/validates, saves draft, and returns messages and remaining issues; integrated into draft lifecycle tests.
Plugin Management & Blacklist
packages/project-builder-server/src/actions/definition/list-plugins.action.ts, .../configure-plugin.action.ts, .../disable-plugin.action.ts, packages/project-builder-server/src/actions/definition/entity-type-blacklist.ts, plus unit tests
Add list-plugins, configure-plugin, disable-plugin actions; introduce BLACKLISTED_ENTITY_TYPES and guard assertEntityTypeNotBlacklisted to block generic operations on plugin.
Entity Search & Listing
packages/project-builder-server/src/actions/definition/search-entities.action.ts, packages/project-builder-server/src/actions/definition/list-entity-types.action.ts, plus unit tests
Add search-entities action (substring, optional type filter); filter out blacklisted types from list-entity-types.
Staging Actions Refactor
packages/project-builder-server/src/actions/definition/stage-create-entity.action.ts, .../stage-update-entity.action.ts, .../stage-delete-entity.action.ts, and corresponding .int.test.ts files
Replace direct draft mutation and old validation with validateAndSaveDraft; enforce entity-type blacklist; centralize issue-to-output mapping and CLI output.
Commit Draft Flow
packages/project-builder-server/src/actions/definition/commit-draft.action.ts
Switch commit validation to fixAndValidateDraftDefinition/validateAndSaveDraft flow; errors block commit, warnings surfaced via mapIssueToOutput.
Test & Test Helpers
packages/project-builder-server/src/actions/__tests__/action-test-utils.ts, packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts
Add invokeServiceActionForTest helper to validate action I/O in tests; centralize definition action fixtures and per-test mocks.
Action Registry & Exports
packages/project-builder-server/src/actions/definition/index.ts, packages/project-builder-server/src/actions/registry.ts
Export and register new actions: applyFixAction, searchEntitiesAction, listPluginsAction, configurePluginAction, disablePluginAction, and stageUpdateEntityAction.
Tests Reorganization
many packages/project-builder-server/src/actions/definition/*.unit.test.ts and .int.test.ts files; removed definition-actions.int.test.ts
Add numerous unit and integration tests for new/changed actions and flows; remove older comprehensive integration test in favor of granular tests.
Changelogs & ESLint
.changeset/mcp-server-actions.md, .changeset/mcp-server-improvements.md, packages/tools/eslint-configs/typescript.js
New changeset entries and ESLint rule adjustments to support test fixtures and extended test blocks.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI/Client
  participant Actions as ServiceAction(apply-fix)
  participant Draft as DraftSession (storage)
  participant Parser as Schema Parser/Validator
  participant Serializer as Serializer
  participant FS as File System

  CLI->>Actions: request apply-fix(project, fixId)
  Actions->>Draft: getOrCreateDraftSession(project)
  Actions->>Parser: build container from draft (parse)
  Parser->>Actions: collect issues with fix metadata
  Actions->>Actions: find issue by fixId
  Actions->>Parser: apply fix to parsed definition (immer produce)
  Actions->>Serializer: serialize fixed definition to name-based format
  Actions->>Parser: fixAndValidateDraftDefinition(fixedSerializedDef)
  Parser->>Actions: return errors/warnings and new container
  Actions->>Draft: validateAndSaveDraft(..., session, projectDir)
  Actions->>FS: write draft-definition.json
  Actions->>CLI: return message + remaining issues
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Make MCP server actions more robust' accurately reflects the main objective of the changeset—improving robustness of MCP server actions through new features like entity search, auto-fix suggestions, plugin management, and validation improvements.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kingston/eng-1096-mcp-server-improvements-and-fixes
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (6)
packages/project-builder-server/src/actions/definition/search-entities.action.ts (1)

52-63: Consider adding result limiting for large entity sets.

The search returns all matching entities without pagination or a result limit. For projects with many entities, this could return unexpectedly large result sets. Consider adding an optional limit parameter to cap results.

💡 Optional enhancement for result limiting
 const searchEntitiesInputSchema = z.object({
   project: z.string().describe('The name or ID of the project.'),
   query: z
     .string()
     .describe('Case-insensitive substring to match against entity names.'),
   entityTypeName: z
     .string()
     .optional()
     .describe('Restrict search to a specific entity type.'),
+  limit: z
+    .number()
+    .int()
+    .positive()
+    .optional()
+    .default(100)
+    .describe('Maximum number of results to return.'),
 });

Then in the handler:

     const results = container.entities
       .filter((entity) => {
         if (input.entityTypeName && entity.type.name !== input.entityTypeName) {
           return false;
         }
         return entity.name.toLowerCase().includes(queryLower);
       })
+      .slice(0, input.limit)
       .map((entity) => ({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/search-entities.action.ts`
around lines 52 - 63, The current search in the results assignment returns all
matches; add an optional numeric limit parameter (e.g., input.limit) and apply
it to cap returned entities to a sane default (e.g., 100) when provided,
validating it is a positive integer; locate the results creation that uses
container.entities.filter(...).map(...) and either slice the filtered array
before mapping or slice the mapped array (use .slice(0, limit)) so only up to
the requested number of items are returned, and update the handler
signature/validation to accept and sanitize input.limit.
packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts (2)

79-79: Replace empty object pattern with underscore placeholder.

Biome flags the empty object pattern {}. Use _ as a conventional placeholder for unused parameters.

♻️ Proposed fix
-  testData: async ({}, use) => {
+  testData: async (_, use) => {
     await use(buildTestData());
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts`
at line 79, The parameter list for the async arrow function assigned to testData
uses an empty object pattern "{}", which Biome flags; replace that with the
conventional unused-parameter placeholder "_" so change the function signature
in testData (the async ({}, use) => handler) to use "_" for the first argument
(async (_, use) =>) to indicate the parameter is intentionally unused.

91-91: Replace empty object pattern with underscore placeholder.

Same issue as above — use _ for the unused parameter.

♻️ Proposed fix
-  projectDir: async ({}, use) => {
+  projectDir: async (_, use) => {
     await use('/test-project');
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts`
at line 91, The async arrow for projectDir is using an empty object pattern for
an unused parameter (projectDir: async ({}, use) => { }); replace the empty
destructuring with a single underscore placeholder (e.g., change the first
parameter to _ or _args) to signal the parameter is intentionally unused; update
the parameter in the projectDir async function definition only.
packages/project-builder-server/src/actions/definition/disable-plugin.action.ts (1)

59-62: Consider using the more common Immer produce signature.

The curried form works but is less readable. The standard two-argument form is clearer:

♻️ Optional: Use standard produce signature
-    // Apply disablePlugin via produce
-    const newDefinition = produce((draft: ProjectDefinition) => {
-      PluginUtils.disablePlugin(draft, input.pluginKey, parserContext);
-    })(container.definition);
+    // Apply disablePlugin via produce
+    const newDefinition = produce(container.definition, (draft) => {
+      PluginUtils.disablePlugin(draft, input.pluginKey, parserContext);
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/disable-plugin.action.ts`
around lines 59 - 62, Replace the curried Immer call with the standard
two-argument produce signature to improve readability: call produce with the
base state first and the drafting function second (use
produce(container.definition, (draft: ProjectDefinition) => {
PluginUtils.disablePlugin(draft, input.pluginKey, parserContext); })), keeping
the same references to PluginUtils.disablePlugin, ProjectDefinition,
input.pluginKey, parserContext and container.definition so behavior is
unchanged.
packages/project-builder-server/src/actions/definition/disable-plugin.action.unit.test.ts (1)

16-25: Consider adding a happy-path test for successful plugin disabling.

Currently only the error case is tested. A test verifying successful disabling would improve coverage and catch regressions in the core flow.

Would you like me to help draft a happy-path test case?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/disable-plugin.action.unit.test.ts`
around lines 16 - 25, Add a happy-path unit test that sets up a project with a
plugin already enabled, invokes disablePluginAction via
invokeServiceActionForTest with { project: 'test-project', pluginKey:
'<enabled-key>' }, and asserts the call resolves successfully; then fetch the
project state (e.g., via the test context's project service/getProject method)
and assert the pluginKey is no longer present in the project's enabled plugins
list. Use the existing test suite for disablePluginAction and mirror the
error-test structure to keep setup/teardown consistent.
packages/project-builder-server/src/actions/definition/stage-delete-entity.action.int.test.ts (1)

75-81: Use name-based lookup instead of array index for robustness.

The first test correctly uses definition.models.some((m) => m.name === 'BlogPost') to verify by name. This test should follow the same pattern instead of assuming models[0] is BlogPost—the index could change if fixtures are modified or additional models are added.

♻️ Suggested fix
     const definition = JSON.parse(defContents) as {
-      models: { model: { fields: { name: string }[] } }[];
+      models: { name: string; model: { fields: { name: string }[] } }[];
     };
-    const blogPost = definition.models[0];
+    const blogPost = definition.models.find((m) => m.name === 'BlogPost');
+    if (!blogPost) throw new Error('BlogPost model not found in definition');
     const fieldNames = blogPost.model.fields.map((f) => f.name);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/definition/stage-delete-entity.action.int.test.ts`
around lines 75 - 81, The test currently assumes the BlogPost model is at
definition.models[0]; update it to find the model by name instead: locate the
block using the variables definition, models, blogPost and fieldNames and
replace the index-based access (definition.models[0]) with a name-based lookup
such as definition.models.find(m => m.name === 'BlogPost'), assert the result is
non-null (throw or expect) and then use that found blogPost.model.fields.map(f
=> f.name) for the same contains/notContains assertions to make the test robust
to fixture ordering changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts`:
- Line 79: The parameter list for the async arrow function assigned to testData
uses an empty object pattern "{}", which Biome flags; replace that with the
conventional unused-parameter placeholder "_" so change the function signature
in testData (the async ({}, use) => handler) to use "_" for the first argument
(async (_, use) =>) to indicate the parameter is intentionally unused.
- Line 91: The async arrow for projectDir is using an empty object pattern for
an unused parameter (projectDir: async ({}, use) => { }); replace the empty
destructuring with a single underscore placeholder (e.g., change the first
parameter to _ or _args) to signal the parameter is intentionally unused; update
the parameter in the projectDir async function definition only.

In
`@packages/project-builder-server/src/actions/definition/disable-plugin.action.ts`:
- Around line 59-62: Replace the curried Immer call with the standard
two-argument produce signature to improve readability: call produce with the
base state first and the drafting function second (use
produce(container.definition, (draft: ProjectDefinition) => {
PluginUtils.disablePlugin(draft, input.pluginKey, parserContext); })), keeping
the same references to PluginUtils.disablePlugin, ProjectDefinition,
input.pluginKey, parserContext and container.definition so behavior is
unchanged.

In
`@packages/project-builder-server/src/actions/definition/disable-plugin.action.unit.test.ts`:
- Around line 16-25: Add a happy-path unit test that sets up a project with a
plugin already enabled, invokes disablePluginAction via
invokeServiceActionForTest with { project: 'test-project', pluginKey:
'<enabled-key>' }, and asserts the call resolves successfully; then fetch the
project state (e.g., via the test context's project service/getProject method)
and assert the pluginKey is no longer present in the project's enabled plugins
list. Use the existing test suite for disablePluginAction and mirror the
error-test structure to keep setup/teardown consistent.

In
`@packages/project-builder-server/src/actions/definition/search-entities.action.ts`:
- Around line 52-63: The current search in the results assignment returns all
matches; add an optional numeric limit parameter (e.g., input.limit) and apply
it to cap returned entities to a sane default (e.g., 100) when provided,
validating it is a positive integer; locate the results creation that uses
container.entities.filter(...).map(...) and either slice the filtered array
before mapping or slice the mapped array (use .slice(0, limit)) so only up to
the requested number of items are returned, and update the handler
signature/validation to accept and sanitize input.limit.

In
`@packages/project-builder-server/src/actions/definition/stage-delete-entity.action.int.test.ts`:
- Around line 75-81: The test currently assumes the BlogPost model is at
definition.models[0]; update it to find the model by name instead: locate the
block using the variables definition, models, blogPost and fieldNames and
replace the index-based access (definition.models[0]) with a name-based lookup
such as definition.models.find(m => m.name === 'BlogPost'), assert the result is
non-null (throw or expect) and then use that found blogPost.model.fields.map(f
=> f.name) for the same contains/notContains assertions to make the test robust
to fixture ordering changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a03dd0fc-6c65-42d6-95e2-74391a6a9443

📥 Commits

Reviewing files that changed from the base of the PR and between 6ee5733 and 010b660.

📒 Files selected for processing (38)
  • .changeset/mcp-server-actions.md
  • .changeset/mcp-server-improvements.md
  • packages/project-builder-lib/src/parser/walk-schema-structure.ts
  • packages/project-builder-lib/src/parser/walk-schema-structure.unit.test.ts
  • packages/project-builder-lib/src/tools/assign-entity-ids.unit.test.ts
  • packages/project-builder-lib/src/tools/entity-service/entity-navigation.ts
  • packages/project-builder-lib/src/tools/entity-service/entity-type-map.ts
  • packages/project-builder-server/src/actions/__tests__/action-test-utils.ts
  • packages/project-builder-server/src/actions/definition/apply-fix.action.ts
  • packages/project-builder-server/src/actions/definition/commit-draft.action.ts
  • packages/project-builder-server/src/actions/definition/configure-plugin.action.ts
  • packages/project-builder-server/src/actions/definition/configure-plugin.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/definition-actions.int.test.ts
  • packages/project-builder-server/src/actions/definition/definition-test-fixtures.test-helper.ts
  • packages/project-builder-server/src/actions/definition/disable-plugin.action.ts
  • packages/project-builder-server/src/actions/definition/disable-plugin.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/draft-lifecycle.int.test.ts
  • packages/project-builder-server/src/actions/definition/entity-type-blacklist.ts
  • packages/project-builder-server/src/actions/definition/get-entity-schema.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/get-entity.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/index.ts
  • packages/project-builder-server/src/actions/definition/list-entities.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/list-entity-types.action.ts
  • packages/project-builder-server/src/actions/definition/list-entity-types.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/list-plugins.action.ts
  • packages/project-builder-server/src/actions/definition/list-plugins.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/search-entities.action.ts
  • packages/project-builder-server/src/actions/definition/search-entities.action.unit.test.ts
  • packages/project-builder-server/src/actions/definition/stage-create-entity.action.int.test.ts
  • packages/project-builder-server/src/actions/definition/stage-create-entity.action.ts
  • packages/project-builder-server/src/actions/definition/stage-delete-entity.action.int.test.ts
  • packages/project-builder-server/src/actions/definition/stage-delete-entity.action.ts
  • packages/project-builder-server/src/actions/definition/stage-update-entity.action.int.test.ts
  • packages/project-builder-server/src/actions/definition/stage-update-entity.action.ts
  • packages/project-builder-server/src/actions/definition/validate-draft.ts
  • packages/project-builder-server/src/actions/definition/validate-draft.unit.test.ts
  • packages/project-builder-server/src/actions/registry.ts
  • packages/tools/eslint-configs/typescript.js
💤 Files with no reviewable changes (1)
  • packages/project-builder-server/src/actions/definition/definition-actions.int.test.ts

@kingston kingston merged commit 05b667f into main Mar 13, 2026
9 of 10 checks passed
@kingston kingston deleted the kingston/eng-1096-mcp-server-improvements-and-fixes branch March 13, 2026 16:05
@github-actions github-actions bot mentioned this pull request Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant